﻿using System;
using System.Collections.Generic;
using AhpilyServer;
using PokerServer.Cache;
using PokerServer.Cache.Fight;
using PokerServer.Model;
using Protocol.Code;
using Protocol.Dto.Fight;

namespace PokerServer.Logic
{
    public class FightHandler : IHandler
    {
        private readonly FightCache fightCache = Caches.Fight;
        private readonly UserCache userCache = Caches.User;

        /// <summary>
        /// 接收到请求的处理
        /// </summary>
        /// <param name="client"></param>
        /// <param name="subCode"></param>
        /// <param name="value"></param>
        public void OnReceive(ClientPeer client, int subCode, object value)
        {
            switch (subCode)
            {
                case FightCode.GRAB_LANDLORD_CREQ:
                    bool result = (bool) value;
                    GrabLandlord(client, result);
                    break;
                case FightCode.DEAL_CREQ:
                    Deal(client, value as DealDto);
                    break;
                case FightCode.PASS_CREQ:
                    PassToNextUser(client);
                    break;
                default:
                    throw new Exception("不期望的请求");
            }
        }


        /// <summary>
        /// 用户点击了不出
        /// </summary>
        /// <param name="clientPeer"></param>
        private void PassToNextUser(ClientPeer clientPeer)
        {
            SingleExecute.Instance.Execute(delegate
            {
                if (userCache.IsOnline(clientPeer) == false)
                {
                    return;
                }

                int userId = userCache.GetId(clientPeer);

                FightRoom room = fightCache.GetRoomByUid(userId);

                // 必须出
                if (room.RoundModel.BiggestUId == userId)
                {
                    clientPeer.Send(OpCode.FIGHT, FightCode.PASS_SRES, -1);
                    return;
                }

                // 可以不出
                clientPeer.Send(OpCode.FIGHT, FightCode.PASS_SRES, 0);

                TurnToNextUser(room);
            });
        }

        /// <summary>
        /// 出牌的处理
        /// </summary>
        /// <param name="clientPeer"></param>
        /// <param name="dealDto"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Deal(ClientPeer clientPeer, DealDto dealDto)
        {
            SingleExecute.Instance.Execute(delegate
            {
                if (userCache.IsOnline(clientPeer) == false)
                {
                    return;
                }

                int userId = userCache.GetId(clientPeer);

                if (userId != dealDto.userid)
                {
                    return;
                }

                FightRoom room = fightCache.GetRoomByUid(userId);

                // 玩家出牌
                // 玩家中途退出
                if (room.LeaveUIdList.Contains(userId))
                {
                    // 下一位出牌
                    TurnToNextUser(room);
                }

                // 玩家还在

                if (room.DeadCard(dealDto.type, dealDto.weight, dealDto.length, dealDto.userid,
                    dealDto.SelectCardLists))
                {
                    // 告诉用户出牌成功
                    clientPeer.Send(OpCode.FIGHT, FightCode.DEAL_SRES, 0);

                    List<CardDto> remainCardList = room.GetPlayerDto(userId).cardList;

                    dealDto.remainCardList = remainCardList;

                    Broadcast(room, OpCode.FIGHT, FightCode.DEAL_BRQ, dealDto);

                    // 检测一下该用户的牌出完了没
                    if (remainCardList.Count == 0)
                    {
                        // game over
                        GameOver(userId, room);
                    }
                    else
                    {
                        // 下一位出牌

                        TurnToNextUser(room);
                    }
                }
                else
                {
                    // 牌太小了 通知客户端
                    clientPeer.Send(OpCode.FIGHT, FightCode.DEAL_SRES, -1);
                }
            });
        }


        /// <summary>
        /// 下一位出牌
        /// </summary>
        /// <param name="room"></param>
        private void TurnToNextUser(FightRoom room)
        {
            int nextUid = room.Turn();

            if (room.IsOffLine(nextUid))
            {
                TurnToNextUser(room);
            }
            else
            {
                // 玩家还在线。 通知他出牌
                // ClientPeer clientPeer = userCache.GetClientPeer(nextUid);
                //
                // clientPeer.Send(OpCode.FIGHT, FightCode.TURN_DEAL_BRQ, nextUid);
                Broadcast(room, OpCode.FIGHT, FightCode.TURN_DEAL_BRQ, nextUid);
            }
        }

        /// <summary>
        /// 用户断开连接的处理
        /// </summary>
        /// <param name="client"></param>
        public void OnDisconnect(ClientPeer client)
        {
            UserLeave(client);
        }

        /// <summary>
        /// 抢地主的处理
        /// </summary>
        /// <param name="clientPeer"></param>
        /// <param name="result"></param>
        private void GrabLandlord(ClientPeer clientPeer, bool result)
        {
            SingleExecute.Instance.Execute(delegate
            {
                if (userCache.IsOnline(clientPeer) == false)
                {
                    return;
                }

                int userId = userCache.GetId(clientPeer);

                FightRoom room = fightCache.GetRoomByUid(userId);

                if (result)
                {
                    // 抢地主
                    room.SetLandlord(userId);
                    // 给每一个客户端发送一个消息， 谁是地主
                    GrabDto grabDto = new GrabDto(userId, room.TableCardList, room.GetUserCards(userId));

                    Broadcast(room, OpCode.FIGHT, FightCode.GRAB_LANDLORD_BRQ, grabDto);
                    Broadcast(room, OpCode.FIGHT, FightCode.TURN_DEAL_BRQ, userId);
                }
                else
                {
                    // 不抢地主
                    int nextUid = room.GetNextUid(userId);
                    Broadcast(room, OpCode.FIGHT, FightCode.TURN_GRAB_BRQ, nextUid);
                }
            });
        }


        /// <summary>
        /// 开始战斗
        /// </summary>
        /// <param name="uidList"></param>
        public void StartFight(List<int> uidList)
        {
            SingleExecute.Instance.Execute(delegate
            {
                // 创建战斗房间
                FightRoom room = fightCache.CreateFightRoom(uidList);

                // 给每个玩家发牌
                room.InitPlayerCards();

                // 给玩家的手牌排序（升序）
                room.Sort();
                // 发送给每一个客户端

                foreach (int uid in uidList)
                {
                    ClientPeer clientPeer = userCache.GetClientPeer(uid);

                    List<CardDto> dtoList = room.GetUserCards(uid);

                    // 可以只发送牌的id
                    clientPeer.Send(OpCode.FIGHT, FightCode.GET_CARD_SRES, dtoList);
                }


                // 开始抢地主的响应
                int firstUserId = room.GetFirstUid();
                Broadcast(room, OpCode.FIGHT, FightCode.TURN_GRAB_BRQ, firstUserId);
            });
        }


        private void UserLeave(ClientPeer clientPeer)
        {
            SingleExecute.Instance.Execute(delegate
            {
                if (userCache.IsOnline(clientPeer) == false)
                {
                    return;
                }

                int userId = userCache.GetId(clientPeer);

                if (fightCache.IsFighting(userId) == false)
                {
                    return;
                }

                FightRoom room = fightCache.GetRoomByUid(userId);

                // 中途退出
                room.LeaveUIdList.Add(userId);

                Broadcast(room, OpCode.FIGHT, FightCode.LEAVE_BRQ, userId);


                // 全部都退出游戏了
                if (room.LeaveUIdList.Count == 3)
                {
                    fightCache.DestroyRoom(room);


                    foreach (int t in room.LeaveUIdList)
                    {
                        UserModel model = userCache.GetModelById(t);

                        model.RunCount += 1;
                        model.Bean -= room.Multiple * Protocol.Constant.Constant.BASE_BEAN_NUMBER;

                        userCache.UpdateUserInfo(model);
                    }
                }
            });
        }


        private void GameOver(int userId, FightRoom room)
        {
            int winIdentity = room.GetPlayerIdentity(userId);

            List<int> winIdList = room.GetSameIdentityUIds(winIdentity);

            // 给胜利的玩家添加胜场
            foreach (int t in winIdList)
            {
                UserModel model = userCache.GetModelById(t);

                model.WinCount++;

                model.Bean += room.Multiple * Protocol.Constant.Constant.BASE_BEAN_NUMBER;
                model.Exp += Protocol.Constant.Constant.ADD_EXP;


                while (model.Lv * 100 <= model.Exp)
                {
                    model.Lv++;
                    model.Exp -= model.Lv * 100;
                }

                userCache.UpdateUserInfo(model);
            }

            List<int> loseUIds = room.GetDifferentIdentityUIds(winIdentity);

            // 更改失败的人的信息
            foreach (int id in loseUIds)
            {
                UserModel model = userCache.GetModelById(id);

                model.Bean -= room.Multiple * Protocol.Constant.Constant.BASE_BEAN_NUMBER;
                model.Exp += Protocol.Constant.Constant.ADD_EXP * 1 / 2;

                while (model.Lv * 100 <= model.Exp)
                {
                    model.Lv++;
                    model.Exp -= model.Lv * 100;
                }

                userCache.UpdateUserInfo(model);
            }

            // 更改逃跑的人的信息
            for (int i = 0; i < room.LeaveUIdList.Count; i++)
            {
                UserModel model = userCache.GetModelById(loseUIds[i]);

                model.RunCount += 1;
                model.Bean -= room.Multiple * Protocol.Constant.Constant.BASE_BEAN_NUMBER;

                while (model.Lv * 100 <= model.Exp)
                {
                    model.Lv++;
                    model.Exp -= model.Lv * 100;
                }

                userCache.UpdateUserInfo(model);
            }


            OverDto overDto = new OverDto(winIdentity, winIdList,
                room.Multiple * Protocol.Constant.Constant.BASE_BEAN_NUMBER);

            Broadcast(room, OpCode.FIGHT, FightCode.OVER_BRQ, overDto);
        }


        /// <summary>
        /// 战斗房间的广播
        /// </summary>
        /// <param name="room">房间对象</param>
        /// <param name="opCode"></param>
        /// <param name="subCode"></param>
        /// <param name="value"></param>
        /// <param name="exClient"></param>
        private void Broadcast(FightRoom room, int opCode, int subCode, object value, ClientPeer exClient = null)
        {
            SocketMsg msg = new SocketMsg(opCode, subCode, value);

            byte[] data = EncodeTool.EncodeMsg(msg);
            byte[] packet = EncodeTool.EncodePacket(data);


            foreach (PlayerDto playerDto in room.PlayerList)
            {
                if (userCache.IsOnline(playerDto.userId))
                {
                    ClientPeer clientPeer = userCache.GetClientPeer(playerDto.userId);

                    if (clientPeer == exClient)
                    {
                        continue;
                    }

                    clientPeer.Send(packet);
                }
            }
        }
    }
}